package com.jh.paymentchannel.repository;

import com.jh.paymentchannel.pojo.PaymentOrderStatistic;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.Tuple;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.CriteriaUpdate;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Root;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * @author Fuchun
 * @since 1.0
 */
public class PaymentOrderStatisticRepositoryImpl extends SimpleJpaRepository<PaymentOrderStatistic, Long>
        implements PaymentOrderStatisticRepositoryCustom {

    private final EntityManager em;

    public PaymentOrderStatisticRepositoryImpl(EntityManager em) {
        super(PaymentOrderStatistic.class, em);
        this.em = em;
    }

    @Override
    @Transactional
    public long setTransferred(List<Long> posIds) {
        if (posIds == null || posIds.isEmpty()) return 0;
        CriteriaBuilder cb = em.getCriteriaBuilder();

        long total = 0;
        for (Long posId : posIds) {
            CriteriaUpdate<PaymentOrderStatistic> criteriaUpdate = cb.createCriteriaUpdate(PaymentOrderStatistic.class);
            Root<PaymentOrderStatistic> root = criteriaUpdate.from(PaymentOrderStatistic.class);
            criteriaUpdate.set("transferred", true).where(cb.equal(root.get("id"), posId));
            Query query = em.createQuery(criteriaUpdate);
            total += query.executeUpdate();
            if (total % 50 == 0) {
                em.flush();
            }
        }
        return total;
    }

    @Override
    public long saveAndFlush(List<PaymentOrderStatistic> posList) {
        if (posList == null || posList.isEmpty()) return 0;
        if (posList.size() < 50) return save(posList).size();
        long total = 0;
        int index = 0;
        for (PaymentOrderStatistic pos : posList) {
            save(pos);
            if (index == 49) {
                flush();
                index = 0;
            }
            index++;
            total++;
        }
        return total;
    }

    @Override
    public Map<Long, Boolean> findIdAndTransferred(Collection<Long> posIds) {
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<Tuple> tupleQuery = cb.createTupleQuery();
        Root<PaymentOrderStatistic> root = tupleQuery.from(PaymentOrderStatistic.class);

        Path<Long> id = root.get("id");
        Path<Boolean> isTransferred = root.get("transferred");
        tupleQuery.select(cb.tuple(id.alias("id"), isTransferred.alias("transferred")))
                .orderBy(cb.asc(id));
        TypedQuery<Tuple> query = em.createQuery(tupleQuery);
        List<Tuple> tuples = query.getResultList();

        Map<Long, Boolean> result = new HashMap<>(posIds.size());
        if (tuples == null || tuples.isEmpty()) {
            for (Long posId : posIds) {
                result.put(posId, false);
            }
        } else {
            for (Tuple tuple : tuples) {
                Long posId = tuple.get("id", Long.class);
                Boolean transferred = tuple.get("transferred", Boolean.class);
                if (posId != null) {
                    result.put(posId, transferred == null ? false : transferred);
                }
            }
            for (Long posId : posIds) {
                if (result.containsKey(posId)) {
                    continue;
                }
                result.put(posId, false);
            }
        }
        return result;
    }
}
